package com.example.leepong.Game.Engines;

import android.util.Log;

import com.example.leepong.Game.Engine;
import com.example.leepong.Game.Entities.Ball;
import com.example.leepong.Game.Entities.Land;
import com.example.leepong.Game.Entities.PlayerProfile;
import com.example.leepong.Game.Entities.Racket;
import com.example.leepong.Game.Entities.Shot;
import com.example.leepong.Game.Shapes.Coordinates;

import java.util.Random;

/**
 * Created by Ichigo on 27/02/14.
 */
public class VirtualPlayer implements IPlayer {
    static Random rnd = new Random();
    private Engine _engine;
    private PlayerProfile _profile;
    private Racket _racket;
    private boolean _isTopSide;

    public VirtualPlayer(Engine engine, PlayerProfile profile) {
        _engine = engine;
        _profile = profile;
    }

    public void Move() {
        Land land = _engine.Land;
        Ball ball = _engine.Ball;

        float racketCenterX = _racket.Rect.CenterX();

        // ball coming
        if (this.IsBallComing(ball)) {
            float moveStep = (land.Rect.Width / _engine.Ups) * _profile.MoveSpeed;
            float estimatedHitX = getEstimatedBallCenterX();
            float dist = estimatedHitX - racketCenterX;
            float absDist = Math.abs(dist);
            if (moveStep < absDist) {
                _racket.Rect.setCenterX(racketCenterX + (dist / (moveStep + absDist)) * moveStep);
            } else {
                _racket.Rect.setCenterX(estimatedHitX);
            }

            // check bounds
            _racket.Rect.Coord.X = Math.max(_racket.Rect.Coord.X, land.Rect.Left);
            _racket.Rect.Coord.X = Math.min(_racket.Rect.Coord.X, land.Rect.Right - _racket.Rect.Width);

        }
        // ball going
        else {
            float moveStep = (land.Rect.Height / _engine.Ups) * _profile.RecenterSpeed;
            float dist = land.Rect.CenterX - racketCenterX;
            if (moveStep < Math.abs(dist)) {
                _racket.Rect.setCenterX(racketCenterX + (Math.signum(dist) * moveStep));
            } else {
                _racket.Rect.setCenterX(land.Rect.CenterX);
            }
        }
    }

    private float getEstimatedBallCenterX() {
        Ball ball = _engine.Ball;

        float dX;
        if (_isTopSide) {
            double tan = Math.tan(((ball.Angle) / 180.0) * Math.PI);
            float difRacketBallY = ball.Rect.Coord.Y - _racket.Rect.Bottom();
            dX = (float) (difRacketBallY / -tan);
        } else {
            double tan = Math.tan(((ball.Angle) / 180.0) * Math.PI);
            float difRacketBallY = _racket.Rect.Top() - ball.Rect.Bottom();
            dX = (float) (difRacketBallY / tan);
        }

        return dX + ball.Rect.CenterX();
    }

    private boolean IsBallComing(Ball ball) {
        if (_isTopSide)
            return ball.IsToTop();
        else
            return ball.IsToBottom();
    }

    @Override
    public void Assign(Racket racket) {
        _racket = racket;
        _isTopSide = _racket == _engine.TopRacket;
    }

    @Override
    public void TouchTap(Coordinates tapCoords) {

    }

    @Override
    public void TouchScroll(Coordinates newCoords, Coordinates oldCoords) {

    }

    @Override
    public void TouchZoneTap(Coordinates tapCoords) {

    }

    @Override
    public void TouchZoneScroll(Coordinates newCoords, Coordinates oldCoords) {

    }

    @Override
    public void Update() {
        this.Move();
    }

    public void Shoot() {
        Ball ball = _engine.Ball;
        ball.MoveStep = ball.MoveStep + ball.MoveStep * _profile.Power;

        ApplyShot(_profile.GetShot());

    }

    private void ApplyShot(Shot shot) {
        Land land = _engine.Land;
        Ball ball = _engine.Ball;

        switch (shot) {
            case Reflect:
                ball.ReflectAngleX();
                break;
            case Counter:
                ball.ReverseAngle();
                break;
            case Long:
                int angle = 90 + rnd.nextInt(10) - 5;
                if (_isTopSide)
                    ball.Angle = angle;
                else
                    ball.Angle = -angle;
                break;

            case Cross:
                ball.MoveStep = ball.MoveStep + (_profile.Power * 0.5f);

                float targetDy = land.Rect.Height - _racket.Rect.Height;
                float targetDx = ball.Rect.CenterX() < land.Rect.CenterX ? land.Rect.Right - ball.Rect.CenterX() : land.Rect.Left - ball.Rect.CenterX();

                double tanAngle = targetDy / targetDx;
                double rawAngle = Math.atan(tanAngle) * 180 / Math.PI;
                ball.Angle = (int) Math.round(rawAngle);

                if (targetDx < 0)
                    ball.Angle = 180 + ball.Angle;

                if (!_isTopSide)
                    ball.Angle = -ball.Angle;

                break;
            default:
                ball.ReflectAngleX();
                break;
        }
    }
}
